/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.ainlolcat.aincalc;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Администратор
 */
public class AinCalc {

    public Cleks vchold;
    public AinCalc(Cleks in){
        vchold = in;
    }

    private boolean ALLOW_STRING_AS_ATTRIBUTE  = true;

    public List parseLeksems(String com){
        //region define state const
        // expects:          lb       digit   symbol  dot     comma     *       )*/
        byte[] r_state =    { 1,        1,      1,      0,      0,      0,      0 };
        byte[] l_state =    { 0,        0,      0,      0,      1,      1,      1 };
        byte[] d_state =    { 0,        1,      0,      1,      1,      1,      1 };
        byte[] c_state =    { 1,        0,      1,      0,      0,      0,      0 };
        byte[] a_state =    { 1,        1,      1,      0,      0,      0,      0 };
        byte[] dot_state =  { 0,        1,      0,      0,      0,      0,      0 };
        byte[] comma_state ={ 1,        1,      1,      0,      0,      0,      0 };

        ArrayList leksems = new ArrayList();
        int right = 0; int left = 0; // счетчики скобок
        int s_pos = -1; // позиция начала команды или числа
        boolean isdot = false; //показывает, что в текущем числе есть точка
        byte[] expect = r_state; // разрешенные состояния, уникальные для каждого

        for (int i = 0; i < com.length(); i++)
        {
            //region bracket
            if (com.charAt(i) == '(')
            {
                if (i != com.length() - 1)
                {
                    if (expect[0] == 1) { leksems.add("("); right++; expect = r_state; }
                    else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected '('"); return null; }
                }
                else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected '('"); return null; }
            }
            if (com.charAt(i) == ')')
            {
                if (expect[6] == 1) { leksems.add(")"); left++; expect = l_state; }
                else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected ')'"); return null; }
            }
            //region Digit
            if (Character.isDigit(com.charAt(i)))
            {
                if (expect[1] == 1)
                {
                    if (s_pos == -1) { s_pos = i; }
                    expect = d_state;
                }
                else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected symbol :" + com.charAt(i)); return null; }

                if (i != com.length() - 1)
                {
                    if (!(Character.isDigit(com.charAt(i+1))))
                    {
                        if (com.charAt(i+1) != '.')
                        {
                            isdot = false;
                            leksems.add(com.substring(s_pos, i+1));
                            s_pos = -1;
                        }
                    }
                }
                else leksems.add(com.substring(s_pos, i+1));
            }
            //region mathematical character
            if (com.charAt(i) == '-')
            {
                if (i != com.length() - 1)
                {
                    if (expect[5] == 1) { leksems.add(com.charAt(i)); expect = a_state; }
                    else
                    {
                        if ((expect[1] == 1)) {leksems.add("-1"); leksems.add("*"); expect = a_state; }
                        else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected symbol :" + com.charAt(i)); return null; }
                    }
                    }
                else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected symbol :" + com.charAt(i)); return null; }
            }

            if ((com.charAt(i) == '+') || (com.charAt(i) == '/') || (com.charAt(i) == '*'))
            {
                if (i != com.length() - 1)
                {
                    if (expect[5] == 1) { leksems.add(com.charAt(i)); expect = a_state; }
                    else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected symbol :" + com.charAt(i)); return null; }
                }
                else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected symbol :" + com.charAt(i)); return null; }
            }
            //region dot
            if (com.charAt(i) == '.')
            {
                if (i != com.length() - 1)
                {
                    if ((expect[3] == 1)&&(!isdot)) { expect = dot_state; isdot = true; }
                    else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected dot"); return null; }
                }
                else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected dot"); return null; }
            }
            //region comma
            if (com.charAt(i) == ',')
            {
                if (i != com.length() - 1)
                {
                    if (expect[4] == 1) { leksems.add(","); expect = comma_state; }
                    else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected comma"); return null; }
                }
                else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected comma"); return null; }
            }
            //region letter
            if (Character.isLetter(com.charAt(i)))
            {
                if (expect[2] == 1)
                {
                    if (s_pos == -1) s_pos = i; // если до этого запоминаем начало команды

                    if (i != com.length() - 1)
                    {
                        if (!(Character.isLetter(com.charAt(i+1)))) // следующий символ не буква.
                        {
                            if (vchold.iscommand(com.substring(s_pos, i+1 )))
                            {
                                leksems.add(com.substring(s_pos, i+1 ));
                                expect = c_state;
                            }
                            else
                            {
                                if (vchold.isconstant(com.substring(s_pos, i+1)))
                                {
                                    leksems.add( vchold.getConstValue(com.substring(s_pos, i+1)));
                                    expect = d_state;
                                }
                                else
                                {
                                    if (vchold.isOutVar(com.substring(s_pos, i+1))){
                                        leksems.add( vchold.getvar(com.substring(s_pos, i+1)));
                                        expect = d_state;
                                    }
                                    else{
                                        if (vchold.isOutCom(com.substring(s_pos, i+1))){
                                            leksems.add(com.substring(s_pos, i+1 ));
                                            expect = c_state;
                                        }
                                        else{
                                            if (ALLOW_STRING_AS_ATTRIBUTE){
                                                leksems.add(com.substring(s_pos, i+1 ));
                                                expect = d_state;
                                            }
                                            else{
                                                Console.WriteLine("Error at " + s_pos + " in line " + com + " : unknown command :" + com.substring(s_pos, i+1));
                                                Console.WriteLine("     you can allow using string as functions parameter.");
                                                return null;
                                            }
                                        }
                                    }
                                }
                            }
                            s_pos = -1;
                        }
                    }
                    else
                    {
                        if (vchold.isconstant(com.substring(s_pos, i+1)))
                        {
                            leksems.add(vchold.getConstValue(com.substring(s_pos, i+1)));
                        }
                        else
                        {
                            if (vchold.isOutVar(com.substring(s_pos, i+1))){
                                leksems.add(vchold.getvar(com.substring(s_pos, i+1)));
                            }
                            else{
                                if (ALLOW_STRING_AS_ATTRIBUTE){
                                    leksems.add(com.substring(s_pos, i+1 ));
                                    expect = d_state;
                                }
                                else{
                                    Console.WriteLine("Error at " + s_pos + " in line " + com + " : unknown command :" + com.substring(s_pos, i+1));
                                    Console.WriteLine("     you can allow using string as functions parameter.");
                                    return null;
                                }
                            }
                        }
                    }
                    //expect = c_state;
                }
                else { Console.WriteLine("Error at " + i + " in line " + com + " : unexpected symbol :" + com.charAt(i)); return null; }
            }
        }
        if (right > left) { Console.WriteLine("Error : ')' expected"); return null; }
        if (right < left) { Console.WriteLine("Error : ')' expected"); return null; }
        //for (int i = 0; i < leksems.Count; i++) Console.WriteLine(leksems[i].ToString());
        return leksems;
    }

    // делает элементарную проверку - проверяет явное нарушение следования символов или неверное написание команд, но не синтаксис
    // бъет на лексемы сообщение и запихивает их в массив.
    public double perform(String com)        {

        LeksemsHandler doit = new LeksemsHandler(vchold);
        return doit.start(parseLeksems(com));
    }
}
